﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using ESpace;

namespace Ord
{
    public class Glob
    {
        /*/// <summary>
        /// Лидирующие города игроков
        /// возвр. массив листов. В массиве 5 эл. - 5 городов 
        /// значение элемента массива - лист из номеров игроков, лидирующих в этом городе
        /// номеров может быть несколько из-за ничьи 
        /// </summary>
        public static List<int>[] LeaderMap(List<Playar> pl)
        {
            List<int>[] ldr = new List<int>[5];

            if (pl.Count == 0) return ldr;
            if (pl.Count == 1) return ldr;

            // пробегаемся по всем городам
            for (int t = 0; t < 5; t++)
            {
                // смотрим лидера в текущем городе (их может быть несколько в случае ничьи)
                // выставляем лидером первого игрока
                Town LeaderT = pl[0].town[t];
                // номера игроков с лидирующим городом
                List<int> n = new List<int>();
                n.Add(0);
                for (int p = 1; p < pl.Count; p++)
                {
                    // сравниваем лидера
                    int ret = LeaderT.Leader(pl[p].town[t]);
                    if (ret == 0) n.Add(p);
                    else if (ret == 2)
                    {
                        n.Clear();
                        n.Add(p);
                        LeaderT = pl[p].town[t];
                    }
                }

                // смотрим лидеров в этом городе
                ldr[t] = n;
            }

            return ldr;
        }
         * */

    }

    public struct CardPoint
    {
        public int NumTown;
        public int NumCardInHand;
        public CardPoint(int NumTown, int NumCardInHand)
        {
            this.NumTown = NumTown;
            this.NumCardInHand = NumCardInHand;
        }
    }

    public class Card
    {
        public enum LeaderStat { Equal, Win, Lose }

        public bool[] troop = new bool[5];

        public int num { get { return GetNumCard(this); } }

        public Card()
        {

        }

        public Card(bool b0, bool b1, bool b2, bool b3, bool b4)
        {
            troop[0] = b0;
            troop[1] = b1;
            troop[2] = b2;
            troop[3] = b3;
            troop[4] = b4;
        }

        public Card(byte b0, byte b1, byte b2, byte b3, byte b4)
        {
            troop[0] = b0 == 1;
            troop[1] = b1 == 1;
            troop[2] = b2 == 1;
            troop[3] = b3 == 1;
            troop[4] = b4 == 1;
        }

        public Card(bool[] b)
        {
            if (b.Length != 5) return;
            for (int i = 0; i < 5; i++)
                troop[i] = b[i];
        }

        int GetNumCard(Card crd)
        {
            return Convert2to10(crd.troop);
        }

        int Convert2to10(bool[] b)
        {
            int ret = 0;
            for (int i = 0; i < b.Length; i++)
                if (b[i]) ret += (int)Math.Pow(2, i);
            return ret;
        }

        public Card Clone()
        {
            Card ret = new Card((bool[])troop.Clone());
            return ret;
        }

        /// <summary>Возвращает, Win - Troop cильнее enTroops, Lose - Troops слабее enTroop, Equal - равны</summary>
        public static LeaderStat CompareTroops(bool[] Troop, bool[] EnTroop)
        {
            // считаем сначала количество
            int cnt = 0;
            int cntEn = 0;
            for (int tr = 0; tr < 5; tr++)
            {
                if (Troop[tr]) cnt++;
                if (EnTroop[tr]) cntEn++;
            }

            if (cnt > cntEn) return LeaderStat.Win;
            if (cntEn > cnt) return LeaderStat.Lose;

            // количество одинаковое. смотрим достоинства
            for (int tr = 4; tr >= 0; tr--)
            {
                if (Troop[tr] && !EnTroop[tr]) return LeaderStat.Win;
                if (!Troop[tr] && EnTroop[tr]) return LeaderStat.Lose;
            }

            return LeaderStat.Equal;

        }

        public override string ToString()
        {
            return (troop[0] ? "U " : "_ ") + (troop[1] ? "U " : "_ ") + (troop[2] ? "U " : "_ ") + (troop[3] ? "U " : "_ ") + (troop[4] ? "U" : "_");
        }

        /// <summary>Вес юнитов</summary>
        public static int GetWeight(bool[] Troop)
        {
            // вес каждого отряда: копейщики - 100, лучники - 101, мечники - 102....
            // если есть 2 и более отряда, числа складываются
            // итого, получаем, что 2 даже самых слабых отряда всехда сильнее одного самого сильного
            int ret = 0;
            for (int tr = 0; tr < 5; tr++)
                if (Troop[tr])
                    ret += 100 + tr;
            return ret;
        }

        /// <summary>Вес юнитов. Берет не более 2-х старших юнитов</summary>
        public static int GetWeight2(bool[] Troop)
        {
            // вес каждого отряда: копейщики - 100, лучники - 101, мечники - 102....
            // если есть 2 и более отряда, числа складываются
            // итого, получаем, что 2 даже самых слабых отряда всехда сильнее одного самого сильного
            int ret = 0;
            for (int tr = 4; tr >= 0; tr--)
            {
                if (Troop[tr]) ret += 100 + tr;
                if (ret >= 200) break;
            }
            return ret;
        }

    }

    public class Town
    {
        public List<Card> card = new List<Card>();

        public enum LeaderStat { Equal, Win, Lose }

        /// <summary>сравнение города с вражеским
        /// возвращает результат сравнения городов на предмет выигрыша</summary>
        public LeaderStat Leader(Town enT)
        {
            // заполняем массивы армии (количество отрядов в каждой армии)
            int[] arm = new int[5];
            int[] enArm = new int[5];

            // пробегаемся по всем армиям
            for (int tr = 0; tr < 5; tr++)
            {
                arm[tr] = 0;
                enArm[tr] = 0;
                // пробегаемся по всем картам
                for (int c = 0; c < card.Count; c++)
                {
                    if (card[c].troop[tr]) arm[tr]++;
                }

                for (int c = 0; c < enT.card.Count; c++)
                {
                    if (enT.card[c].troop[tr]) enArm[tr]++;
                }
            }

            // сравниваем лидирующие армии 
            LeaderStat ret = CompareArm(4, arm, enArm);
            if (ret != 0) return ret;
            // сравниваем тройки
            ret = CompareArm(3, arm, enArm);
            if (ret != 0) return ret;
            // сравниваем двойки
            ret = CompareArm(2, arm, enArm);
            if (ret != 0) return ret;
            // сравниваем единицы
            ret = CompareArm(1, arm, enArm);
            if (ret != 0) return ret;

            return 0;
        }

        /// <summary>Возвращает накапливаемые линии (отряды, где можно накопить минимум kolVoTroops)</summary>
        public bool[] GetCollectedTroops(int kolVoTroops)
        {
            if (card.Count == 0) return null;

            // счетчики
            int[] mas = new int[5] { -1, -1, -1, -1, -1 };

            // пробегаемся по всем картам и устанавливаем начальный счетчик допустимых пропусков для всех картинок и -1, если картинки нет
            foreach (Card crd in card)
            {
                for (int tr = 0; tr < 5; tr++) if (mas[tr] == -1) mas[tr] = crd.troop[tr] ? (4 - kolVoTroops) : -1;
            }

            // пробегаемся по всем картам. Если в текущей карте нет требуемого бойца, уменьшаем соответствующий счетчик на 1, 
            for (int i = 0; i < card.Count; i++)
            {
                for (int tr = 0; tr < 5; tr++)
                    if (!card[i].troop[tr]) mas[tr]--;
            }

            // возможно накопить линии только в тех отрядах, где счетчик остался >= 0
            bool[] ret = new bool[5];
            for (int tr = 0; tr < 5; tr++) ret[tr] = mas[tr] >= 0;

            return ret;
        }

        /// <summary>Возвращает L*1000+N где L - размер линий, N - вес накапливаемых линий</summary>
        public int GetCollectedLinesWeight()
        {
            if (card.Count == 0) return 0;

            int L = 4;
            int CltWeight = Card.GetWeight(GetCollectedTroops(4));
            if (CltWeight != 0) return L * 1000 + CltWeight;

            L = 3;
            CltWeight = Card.GetWeight(GetCollectedTroops(3));
            if (CltWeight != 0) return L * 1000 + CltWeight;

            L = 2;
            CltWeight = Card.GetWeight(GetCollectedTroops(2));
            if (CltWeight != 0) return L * 1000 + CltWeight;

            return 1 * Card.GetWeight(GetCollectedTroops(1));
        }

        /// <summary>Возвращает L*1000+N где L - размер линий, N - вес накапливаемых линий. Берет не более 2-х старших линий</summary>
        public int GetCollectedLinesWeight2()
        {
            if (card.Count == 0) return 0;

            int L = 4;
            int CltWeight = Card.GetWeight2(GetCollectedTroops(4));
            if (CltWeight != 0) return L * 1000 + CltWeight;

            L = 3;
            CltWeight = Card.GetWeight2(GetCollectedTroops(3));
            if (CltWeight != 0) return L * 1000 + CltWeight;

            L = 2;
            CltWeight = Card.GetWeight2(GetCollectedTroops(2));
            if (CltWeight != 0) return L * 1000 + CltWeight;

            return 1 * Card.GetWeight2(GetCollectedTroops(1));
        }

        /// <summary>
        /// Сравниваем армии. numArm - положение армии (4 - лидирующая, 3 - тройки ....)
        /// возвращает: 0 - равны, 1 - выигр, 2 - проигр
        /// </summary>
        LeaderStat CompareArm(int numArm, int[] arm, int[] enArm)
        {
            // сравниваем количество армий
            int kolvo = 0;
            int enKolvo = 0;
            for (int i = 0; i < 5; i++)
            {
                if (arm[i] == numArm) kolvo++;
                if (enArm[i] == numArm) enKolvo++;
            }
            if (kolvo != enKolvo)
            {
                if (kolvo > enKolvo) return LeaderStat.Win;
                else return LeaderStat.Lose;
            }
            // сравниваем порядок армий (если они есть)
            if (kolvo > 0)
            {
                for (int i = 4; i >= 0; i--)
                {
                    // у обоих армии лидирующие
                    if (arm[i] == numArm && enArm[i] == numArm) continue;
                    // нет лидирующих армий
                    if (arm[i] != numArm && enArm[i] != numArm) continue;

                    // если есть лид армия у кого-то, а у другого ее нет, возвращаем ответ
                    if (arm[i] == numArm || enArm[i] == numArm)
                    {
                        if (arm[i] == numArm) return LeaderStat.Win;
                        else return LeaderStat.Lose;
                    }
                }
            }
            return LeaderStat.Equal;
        }

        public Town Clone()
        {
            Town ret = new Town();
            foreach (Card crd in card)
                ret.card.Add(crd.Clone());
            return ret;
        }

        public override string ToString()
        {
            bool[] b = GetCollectedTroops(4);
            if (b == null) return "пустой";
            return (b[0] ? "U " : "_ ") + (b[1] ? "U " : "_ ") + (b[2] ? "U " : "_ ") + (b[3] ? "U " : "_ ") + (b[4] ? "U" : "_");
        }

    }

    public class Player
    {
        public String Name;
        public Town[] town = new Town[5];
        public List<Card> card = new List<Card>();
        public Point LastCard = new Point(-1, -1);
        public float cntTimeLastCard = 0; // для анимации затухания последней карты
        public bool isBot { get { return botInd != -1; } } // игрок - это бот
        public int botInd = -1;
        public Player(String Name)
        {
            this.Name = Name;
            for (int i = 0; i < 5; i++)
                town[i] = new Town();
        }
        public Player(String Name, int botInd)
        {
            this.Name = Name;
            for (int i = 0; i < 5; i++)
                town[i] = new Town();
            this.botInd = botInd;
        }
        /// <summary>
        /// рассчитать какую карту выложить и в какой город (для компа)
        /// город - X, карта - Y
        /// </summary>
        public Point CardInTown()
        {
            Point p = new Point(-1, -1);

            #region *** если все города заполнены, выходим ********
            bool bExit = true;
            for (int t = 0; t < 5; t++)
            {
                if (town[t].card.Count < 4)
                {
                    bExit = false;
                    break;
                }
            }
            if (bExit) return p;
            #endregion *** если все города заполнены, выходим ********

            #region *** рассчитаем силу каждой карты **********
            // уровень каждого войска: копейщики - 10, лучники - 11, мечники - 12....
            int[] sCard = new int[5];
            int numMaxCard = 0; // номер самой сильной карты
            int max = 0;
            for (int c = 0; c < card.Count; c++)
            {
                sCard[c] = 0;
                for (int tr = 0; tr < 5; tr++)
                {
                    if (card[c].troop[tr]) sCard[c] += 10 + tr;
                }
                // смотрим самую сильную карту
                if (sCard[c] > max)
                {
                    max = sCard[c];
                    numMaxCard = c;
                }
            }
            #endregion *** рассчитаем силу каждой карты **********

            #region *** eсли есть пустые города, заполняем их самыми сильными картами ***
            for (int t = 0; t < 5; t++)
            {
                // если город пуст, кладем в него самую сильную карту из руки
                if (town[t].card.Count == 0)
                {
                    p.X = t;
                    p.Y = numMaxCard;
                    return p;
                }
            }
            #endregion *** eсли есть пустые города, заполняем их самыми сильными картами ***

            #region *** смотрим каждую карту. Насколько она подходит каждому городу ******
            // 0 - не подходит, 10 - дополняет ведущую армию копейщиков, 11 - доп вед арм лучников. если доп 2 вед армии, складываем
            // i - город, j - карта
            int[,] MapCard = new int[5, card.Count];
            // пробегаемся по всем городам
            for (int t = 0; t < 5; t++)
            {
                // смотрим ведущие армии
                bool[] TopArm = new bool[5];
                for (int tr = 0; tr < 5; tr++)
                {
                    TopArm[tr] = true;
                    // пробегаемся по всем картам в городе
                    // если хотя бы в одной карте нет нужной армии, значит армия не ведущая
                    for (int c = 0; c < town[t].card.Count; c++)
                    {
                        if (!town[t].card[c].troop[tr])
                        {
                            TopArm[tr] = false;
                            break;
                        }
                    }
                }

                // смотрим, насколько подходит каждая карта в руке к этому городу
                for (int c = 0; c < card.Count; c++)
                {
                    MapCard[t, c] = 0;
                    // пробегаемся по всем армиям карты. Если армия совпадает с вед армией города, добавляем коэф
                    for (int tr = 0; tr < 5; tr++)
                    {
                        if (card[c].troop[tr] && TopArm[tr]) MapCard[t, c] += 10 + tr;
                    }
                }
            }
            #endregion ***  смотрим каждую карту. Насколько она подходит каждому городу ******

            #region *** смотрим наиболее подходящую карту для выкладывания ***
            max = -1;
            for (int t = 0; t < 5; t++)
            {
                // если город заполнен, переходим к следующему
                if (town[t].card.Count >= 4) continue;
                // пробегаемся по всем картам в руке
                for (int c = 0; c < card.Count; c++)
                {
                    if (max < MapCard[t, c])
                    {
                        max = MapCard[t, c];
                        p.X = t;
                        p.Y = c;
                    }
                }
            }
            #endregion *** смотрим наиболее подходящую карту для выкладывания ***

            return p;
        }

    }

    public class Game
    {
        public String Name; // имя игры
        public String Moderator; // uin модератора
        public bool GameIsPlay = false; // игра запущена и играется

        public Byte PlayPl = 0; // игрок, который сейчас ходит
        public List<Player> pl = new List<Player>(); // список юзеров в игре

        public Game(String Name, String Moderator)
        {
            this.Name = Name;
            this.Moderator = Moderator;
        }

        public Game(BufReader br)
        {
            Name = br.ReadString(); // имя игры
            Moderator = br.ReadString(); // модератор игры
            GameIsPlay = br.ReadByte() == 1; // игра запущена
            PlayPl = br.ReadByte();

            // список игроков
            int kolvoUsers = br.ReadByte(); // количество юзверей
            for (int i = 0; i < kolvoUsers; i++)
            {
                String nmPl = br.ReadString();
                int botInd = br.ReadInt();
            }
        }

        public void BoildPack(BufWriter bw)
        {
            bw.WriteString(Name); // имя игры
            bw.WriteString(Moderator); // модератор
            bw.WriteByte((byte)(GameIsPlay ? 1 : 0)); // игра запущена
            bw.WriteByte(PlayPl); // игрок, который сейчас ходит
            // список игроков
            bw.WriteInt(pl.Count); // количество игроков в игре
            for (int i = 0; i < pl.Count; i++)
            {
                bw.WriteString(pl[i].Name);
                // бот
                bw.WriteInt(pl[i].botInd);
            }
        }

        public int GetPlayarIndByName(String PlayarName)
        {
            if (PlayarName == "") return -1;
            for (int i = 0; i < pl.Count; i++)
                if (pl[i].Name == PlayarName)
                    return i;
            return -1;
        }

    }


}
